package com.hendraanggrian.appcompat.widget;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.agp.components.Text;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Utility class to help implement {@link SocialView} on any {@link Text}-based class.
 * This class itself is not a {@link ohos.agp.components.Component}.
 */
public final class SocialViewHelper implements SocialView {
    private static final int FLAG_HASHTAG = 1;
    private static final int FLAG_MENTION = 2;
    private static final int FLAG_HYPERLINK = 4;
    private final Text view;
    private Pattern hashtagPattern;
    private Pattern mentionPattern;
    private Pattern hyperlinkPattern;
    private int flags;
    private OnChangedListener hashtagChangedListener;
    private OnChangedListener mentionChangedListener;
    private boolean hashtagEditing;
    private boolean mentionEditing;

    /**
     * Configuring {@link SocialView} into given view.
     *
     * @param view Text
     * @return SocialViewHelper对象
     */
    public static SocialView install(Text view) {
        return new SocialViewHelper(view, null);
    }

    /**
     * Configuring {@link SocialView} into given view.
     *
     * @param view Text
     * @param attrs AttrSet
     * @return SocialViewHelper对象
     */
    public static SocialView install(Text view, AttrSet attrs) {
        return new SocialViewHelper(view, attrs);
    }

    @SuppressWarnings("FieldCanBeLocal")
    private final Text.TextObserver textWatcher = new Text.TextObserver() {
        @Override
        public void onTextUpdated(String s, int start, int before, int count) {
            if (s.length() == 0) {
                return;
            }
            if (s.length() > 10) {
                recolorize();
            }
            if (start < s.length()) {
                final int index = start + count - 1;
                if (index < 0) {
                    return;
                }
                switch (s.charAt(index)) {
                    case '#':
                        hashtagEditing = true;
                        mentionEditing = false;
                        break;
                    case '@':
                        hashtagEditing = false;
                        mentionEditing = true;
                        break;
                    default:
                        if (!Character.isLetterOrDigit(s.charAt(start))) {
                            hashtagEditing = false;
                            mentionEditing = false;
                        } else if (hashtagChangedListener != null && hashtagEditing) {
                            hashtagChangedListener.onChanged(SocialViewHelper.this, s.subSequence(
                                    indexOfPreviousNonLetterDigit(s, 0, start) + 1, start + count
                            ));
                        } else if (mentionChangedListener != null && mentionEditing) {
                            mentionChangedListener.onChanged(SocialViewHelper.this, s.subSequence(
                                    indexOfPreviousNonLetterDigit(s, 0, start) + 1, start + count
                            ));
                        }
                        break;
                }
            }
        }
    };

    private SocialViewHelper(Text view, AttrSet attrs) {
        this.view = view;
        view.addTextObserver(textWatcher);
        view.setText(view.getText());
        recolorize();
    }

    @Override
    public Pattern getHashtagPattern() {
        return hashtagPattern != null ? hashtagPattern : Pattern.compile("#(\\w+)");
    }

    @Override
    public Pattern getMentionPattern() {
        return mentionPattern != null ? mentionPattern : Pattern.compile("@(\\w+)");
    }

    @Override
    public Pattern getHyperlinkPattern() {
        return hyperlinkPattern != null ? hyperlinkPattern : PatternsCompat.WEB_URL;
    }

    @Override
    public void setHashtagPattern(Pattern pattern) {
        if (hashtagPattern != pattern) {
            hashtagPattern = pattern;
            recolorize();
        }
    }

    @Override
    public void setMentionPattern(Pattern pattern) {
        if (mentionPattern != null) {
            mentionPattern = pattern;
            recolorize();
        }
    }

    @Override
    public void setHyperlinkPattern(Pattern pattern) {
        if (hyperlinkPattern != null) {
            hyperlinkPattern = pattern;
            recolorize();
        }
    }

    @Override
    public boolean isHashtagEnabled() {
        return (flags | FLAG_HASHTAG) == flags;
    }

    @Override
    public boolean isMentionEnabled() {
        return (flags | FLAG_MENTION) == flags;
    }

    @Override
    public boolean isHyperlinkEnabled() {
        return (flags | FLAG_HYPERLINK) == flags;
    }

    @Override
    public void setHashtagEnabled(boolean enabled) {
        if (enabled != isHashtagEnabled()) {
            flags = enabled ? flags | FLAG_HASHTAG : flags & (~FLAG_HASHTAG);
            recolorize();
        }
    }

    @Override
    public void setMentionEnabled(boolean enabled) {
        if (enabled != isMentionEnabled()) {
            flags = enabled ? flags | FLAG_MENTION : flags & (~FLAG_MENTION);
            recolorize();
        }
    }

    @Override
    public void setHyperlinkEnabled(boolean enabled) {
        if (enabled != isHyperlinkEnabled()) {
            flags = enabled ? flags | FLAG_HYPERLINK : flags & (~FLAG_HYPERLINK);
            recolorize();
        }
    }

    @Override
    public void setOnHashtagClickListener(OnClickListener listener) {
        ensureMovementMethod(listener);
        recolorize();
    }

    @Override
    public void setOnMentionClickListener(OnClickListener listener) {
        ensureMovementMethod(listener);
        recolorize();
    }

    @Override
    public void setOnHyperlinkClickListener(OnClickListener listener) {
        ensureMovementMethod(listener);
        recolorize();
    }

    @Override
    public void setHashtagTextChangedListener(OnChangedListener listener) {
        hashtagChangedListener = listener;
    }

    @Override
    public void setMentionTextChangedListener(OnChangedListener listener) {
        mentionChangedListener = listener;
    }


    @Override
    public List<String> getHashtags() {
        return listOf(view.getText(), getHashtagPattern(), false);
    }

    @Override
    public List<String> getMentions() {
        return listOf(view.getText(), getMentionPattern(), false);
    }

    @Override
    public List<String> getHyperlinks() {
        return listOf(view.getText(), getHyperlinkPattern(), true);
    }

    private void ensureMovementMethod(Object listener) {
    }

    private void recolorize() {
        view.getText();
    }

    private static int indexOfPreviousNonLetterDigit(CharSequence text, int start, int end) {
        for (int i = end; i > start; i--) {
            if (!Character.isLetterOrDigit(text.charAt(i))) {
                return i;
            }
        }
        return start;
    }

    /**
     * 从provider点击选择内容进行文字分割
     *
     * @param text 内容
     * @return 内容
     */
    @Override
    public CharSequence terminateToken(CharSequence text) {
        Collection<Character> chars = new ArrayList<>();
        chars.add('#');
        chars.add('@');
        int i = text.length();
        while (i > 0 && text.charAt(i - 1) == ' ') {
            i--;
        }
        if (i > 0 && chars.contains(text.charAt(i - 1))) {
            return text;
        } else {
            return text + " ";
        }
    }

    @Override
    public int findTokenStart(CharSequence text, int cursor, ListContainer listContainer) {
        Collection<Character> chars = new ArrayList<>();
        chars.add('#');
        chars.add('@');
        int i = cursor;

        while (i > 0 && !chars.contains(text.charAt(i - 1))) {
            i--;
        }
        while (i < cursor && text.charAt(i) == ' ') {
            i++;
        }
        if (i == 0) {
            listContainer.setVisibility(Component.HIDE);
        } else {
            listContainer.setVisibility(Component.VISIBLE);
        }
        return i;
    }

    private static List<String> listOf(CharSequence text, Pattern pattern, boolean isHyperlink) {
        final List<String> list = new ArrayList<>();
        final Matcher matcher = pattern.matcher(text);
        while (matcher.find()) {
            list.add(matcher.group(!isHyperlink
                    ? 1
                    : 0));
        }
        return list;
    }

}